home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Mousetools / AKeySwap / AKeySwap.c < prev    next >
C/C++ Source or Header  |  1996-09-26  |  12KB  |  373 lines

  1. /*
  2.  
  3. AKeySwap.c
  4. Version 1.1 by John Fieber
  5. 05 Feb 1993
  6.  
  7. v1.1 05 Feb 1993 fixed a tiny "sanity check" bug
  8. v1.0 31 Jan 1993 first polished version
  9. v0.9 16 Dec 1992 first quick-and-dirty version
  10.  
  11.  
  12. 1. What is AKeySwap
  13.  
  14. The two `Amiga' keys to the left and right of the spacebar serve
  15. different functions in the Amiga system.  The right key is used for
  16. menu shortcuts while the left is reserved for system use, such as
  17. flipping through screens.
  18.  
  19. The menu shortcut key is the most frequently used by most people but
  20. some find it awkward to use.  AKeySwap swaps the the left and right
  21. Amiga key functions so you can use the left Amiga key as the menu
  22. shortcut key.  Correspondingly, the right Amiga key becomes the
  23. system key.
  24.  
  25.  
  26. 2. Installation and Use
  27.  
  28. Because AKeySwap is a commodity, you need at least version 2.04 of
  29. the operating system.
  30.  
  31. Using AKeySwap is trivial.  Just double click on the icon.  For
  32. `permanent' installation, drop it in your WBStartup drawer.  Be sure
  33. that the DONOTWAIT tooltype is present.  Alternately, you may run it
  34. from a shell or your User-Startup.  It runs as a background task so
  35. there is no need to "RUN >NIL: <NIL" it.
  36.  
  37. For optimal results, AKeySwap should be one of the very first
  38. programs in the input event chain and thus the default CX_PRIORITY is
  39. 100.  If you need to adjust this, you may put "CX_PRIORITY=<value>" in
  40. the icon tooltypes or specify it on the command line.
  41.  
  42. As with all commodities, you can activate, deactivate or remove
  43. AKeySwap by using the Exchange program supplied by Commodore.  You
  44. can also remove AKeySwap by running it a second time or sending a
  45. Control-C break to its process.
  46.  
  47.  
  48. 3. Credits and Comments
  49.  
  50. This program was written at the suggestion of David Salamon.
  51.  
  52. Please feel free to distribute and modify this under the following
  53. conditions:
  54.  
  55. · The source code, documentation and executable must be distributed
  56.   together.
  57.  
  58. · Any changes to the source, documentation or executable must be
  59.   documented with what was change and who performed the change.
  60.  
  61. · Do make an attempt to contact me about the change.  See below.
  62.  
  63. Send comments suggestions to:
  64.  
  65.    jfieber@sophia.smith.edu
  66.  
  67. or for postal users:
  68.  
  69.    John Fieber
  70.    14 Conz
  71.    Northampton MA 01060-3861
  72.    USA
  73.  
  74. */
  75.  
  76.  
  77. /*
  78. Here is the soruce code.  This was written to be compiled with the
  79. SAS/C 6.x compiler and linked with the cback.o startup module.
  80.  
  81. The defaults in the SCOPTIONS file call for a gst which is not
  82. included in the archive.  Just supply the MAKEGST=AKeySwap.gst option
  83. to rebuild it.
  84.  
  85. Some basic "who we are" #defines and a version string to be found by
  86. the `Version' shell command.
  87. */
  88.  
  89. #define PROGNAME "AKeySwap"
  90. #define PROGVERSION "1.1"
  91. #define PROGDATE "(5.2.93)"
  92.  
  93. static char *VersionTag = "\0$VER: " PROGNAME " " PROGVERSION " " PROGDATE;
  94.  
  95.  
  96. /*
  97. Here are the include files for the system interface.
  98. */
  99.  
  100. #include <stdlib.h>
  101. #include <exec/exec.h>
  102. #include <dos/dos.h>
  103. #include <libraries/commodities.h>
  104. #include <workbench/icon.h>
  105. #include <devices/inputevent.h>
  106.  
  107. /*
  108. To accompany the previous includes, Commodore provides us with
  109. C function prototypes for all the system funtions.  SAS throws in
  110. #pragmas for the functions and declares the library base pointers so
  111. that the startup code will automagically open the necessary
  112. libraries.
  113. */
  114.  
  115. #include <proto/exec.h>
  116. #include <proto/dos.h>
  117. #include <proto/commodities.h>
  118. #include <proto/icon.h>
  119. #include <clib/alib_protos.h>
  120.  
  121.  
  122. /*
  123. While we are at it, how about prototypes for functions defined in
  124. this module...all two of them.
  125. */
  126.  
  127. void __saveds swap_keys(CxMsg *cxmessage, CxObj *cxobject);
  128. void process_events(void);
  129.  
  130.  
  131. /*
  132. The SAS/C cback.o startup module uses this information to detach this
  133. program from the shell if you wish to start from one.  Since this
  134. process doesn't actually do much of anything, we can save some memory
  135. on the stack.
  136. */
  137.  
  138. long __stack = 1024L;  /* How much stack we need */
  139. char *__procname = PROGNAME; /* What the process should be called */
  140. long __BackGroundIO = 0L; /* Do we want to allow for some output */
  141.  
  142. /*
  143. Some more SAS/C specific stuff.  The startup code will automagically
  144. open any libraries whose bases are declared (NOT defined!).  This
  145. variable indicates the minimum library version that we need.
  146. */
  147.  
  148. long __oslibversion = 37;
  149.  
  150.  
  151. /*
  152. The commodities system works by having applications (such as this)
  153. register with the system by using a broker.  When the input device
  154. gets an input event, it passes it off to the commodities system which
  155. in turn passes it to all the active brokers.  To register our broker,
  156. we need a NewBroker structure.
  157.  
  158. The NBU_UNIQUE flag means that only one broker with our name is
  159. allowed in the system.  The commodities system will refuse to create
  160. another one, thus the call to CxBroker will fail.  The NBU_NOTIFY
  161. command informs that if the above happens, the commodities system
  162. should notify the existing broker of the event.
  163. */
  164.  
  165. struct NewBroker newbroker = {
  166.     NB_VERSION,
  167.     PROGNAME,
  168.     PROGNAME " " PROGVERSION,
  169.     "Swaps left and right Amiga keys",
  170.     NBU_UNIQUE | NBU_NOTIFY,
  171.     0,
  172.     0,
  173.     NULL,
  174.     0
  175. };
  176.  
  177. /*
  178. Pointers to our commodities opjects.
  179. */
  180.  
  181. CxObj *broker;
  182. CxObj *custom;
  183.  
  184. /*
  185. Another thing we need is a message port through which the commodities
  186. system can communicate with our task.
  187. */
  188.  
  189. struct MsgPort *broker_message_port; /* where we get message from */
  190.  
  191.  
  192. /*
  193. User supplied parameters should always be subjected to some sort of
  194. sanity check.  In this case, we want to make sure the CX_PRIORITY
  195. option is reasonable.  These macros will ensure that.
  196. */
  197.  
  198. #define max(a,b) ((a) > (b) ? (a) : (b))
  199. #define min(a,b) ((a) <= (b) ? (a) : (b))
  200. #define range_check(user_value, a, b) max(min(user_value, max(a,b)), min(a,b))
  201.  
  202.  
  203. /*
  204. And this is the default, maximum and minimum values for the
  205. CX_PRIORITY parameter.
  206. */
  207.  
  208. #define DEFAULT_CX_PRIORITY 100
  209. #define MAX_CX_PRIORITY 127
  210. #define MIN_CX_PRIORITY -128
  211.  
  212.  
  213. /*
  214. And here is the main function.  Basically we set up our commodity and
  215. then go to sleep while waiting for messages from the commodities
  216. system.
  217. */
  218.  
  219. main(int argc, char *argv[])
  220. {
  221.     UBYTE **user_parameters;
  222.     CxMsg *msg; /* a scratch message pointer */
  223.     int error = 20; /* reset to 0 if everything goes okay */
  224.  
  225.     /* Get the user's parameters */
  226.     user_parameters = ArgArrayInit(argc, argv);
  227.  
  228.     /* Try to create the broker.  Start by creating a message port */
  229.     if (broker_message_port = CreateMsgPort()) {
  230.         newbroker.nb_Port = broker_message_port;
  231.         /* Set the broker priority */
  232.         newbroker.nb_Pri = (BYTE) ArgInt(user_parameters, "CX_PRIORITY", DEFAULT_CX_PRIORITY);
  233.         newbroker.nb_Pri = range_check(newbroker.nb_Pri, MAX_CX_PRIORITY, MIN_CX_PRIORITY);
  234.         if (broker = CxBroker(&newbroker, NULL)) {
  235.             if (custom = CxCustom(swap_keys, 0L)) {
  236.                 AttachCxObj(broker, custom);
  237.                 ActivateCxObj(broker, TRUE);
  238.                 process_events();
  239.                 error = 0;
  240.             }
  241.             /* Clean the broker and all attached objects */
  242.             DeleteCxObjAll(broker);
  243.         }
  244.         /* Empty the message port and delete it */
  245.         while(msg = (CxMsg *) GetMsg(broker_message_port))
  246.             ReplyMsg((struct Message *) msg);
  247.         DeleteMsgPort(broker_message_port);
  248.     }
  249.     ArgArrayDone();
  250.     exit(error);
  251. }
  252.  
  253.  
  254. /*
  255. Processing events is trivial.  The only thing we have to deal with is
  256. messages telling us to go away and messages telling us to either
  257. activate or de-activate.  The action of this commodity happens
  258. entirely in its objects which run under a different task.  This task
  259. just sets everything up and the goes into hibernation.
  260. */
  261.  
  262. void process_events(void)
  263. {
  264.     ULONG signal_received; /* what signals did we get? */
  265.     BOOL finished = FALSE; /* should we quit? */
  266.     CxMsg *message;        /* a commodities message */
  267.     ULONG message_type;    /* what sort of a message is it? */
  268.     ULONG message_id;      /* what sort is it really? */
  269.  
  270.     while (!finished) {
  271.         /* Wait for a signal... */
  272.         signal_received = Wait(SIGBREAKF_CTRL_C | 1L << broker_message_port->mp_SigBit);
  273.  
  274.         while((message = (CxMsg *) GetMsg(broker_message_port)) && (!finished)) {
  275.             message_type = CxMsgType(message);
  276.             message_id = CxMsgID(message);
  277.             ReplyMsg((struct Message *) message);
  278.             if (message_type == CXM_COMMAND) {
  279.                 switch(message_id) {
  280.                     case CXCMD_DISABLE: /* disable ourself */
  281.                         ActivateCxObj(broker, 0L);
  282.                         break;
  283.                     case CXCMD_ENABLE: /* enable ourself */
  284.                         ActivateCxObj(broker, 1L);
  285.                         break;
  286.                     case CXCMD_KILL: /* vanish! (poof!) */
  287.                     case CXCMD_UNIQUE:
  288.                         finished = TRUE;
  289.                         break;
  290.                 }
  291.             }
  292.         }
  293.         /* Check for a break signal */
  294.         if (signal_received & SIGBREAKF_CTRL_C)
  295.             finished = TRUE;
  296.     }
  297. }
  298.  
  299. /*
  300. Up till now, most everything has been "stage-setting".  This is the
  301. function where the program actually does it's work.  The commodities
  302. system calls this function when our custom object receives a
  303. commodities message.  Do note that this function runs under the
  304. input.device task.  As such, we must be very conservative in our
  305. stack usage, and most importantly, realize that we are running in a
  306. *task*, not a process and thus we cannot use anything from the
  307. dos.library.  For the purposes of this program neither the stack nor
  308. the task limitation are issues.
  309.  
  310. Anyway, on to the point.  This function has to do two main things
  311. when it receives a commodities message.  The first must be done for
  312. any keystroke and that is to look at the qualfier field of the input
  313. event contained in the message and swap the states of the two Amiga
  314. keys.  The ie_Qualifier field is a bit field and the positions left
  315. and right qualifier keys, including shift and alternate, are adjacent
  316. so flipping them can be easily done with bitshifts.
  317.  
  318. The second task is that when one of the Amiga keys goes either up or
  319. down, a message is generated in which the code field of the input
  320. event is for the Amiga key that was hit.  We have to change these too.
  321. */
  322.  
  323. /* The raw key codes. */
  324. #define RAW_LAMIGA  102
  325. #define RAW_RAMIGA  103
  326.  
  327.  
  328. /* And the bitmasks for the ie_Qualifier field. */
  329. #define LEFT_QUALIFIER_MASK (IEQUALIFIER_LCOMMAND)
  330. #define RIGHT_QUALIFIER_MASK (IEQUALIFIER_RCOMMAND)
  331.  
  332. /* Note the __saveds.  This is required as this function is called
  333.    from another task. */
  334.  
  335. void __saveds swap_keys(CxMsg *cxmessage, CxObj *cxobject)
  336. {
  337.     register struct InputEvent *ie;
  338.     register UWORD left_qualifiers;
  339.     register UWORD right_qualifiers;
  340.     UWORD upstroke;
  341.  
  342.     ie = (struct InputEvent *) CxMsgData(cxmessage);
  343.     while(ie) {
  344.         left_qualifiers = ie->ie_Qualifier & LEFT_QUALIFIER_MASK;
  345.         right_qualifiers = ie->ie_Qualifier & RIGHT_QUALIFIER_MASK;
  346.         /* If either qualifier is present, do the flip */
  347.         if (left_qualifiers || right_qualifiers) {
  348.             /* Flip the Amiga keys in the ie_Qualifier field */
  349.             ie->ie_Qualifier &= ~(LEFT_QUALIFIER_MASK | RIGHT_QUALIFIER_MASK);
  350.             ie->ie_Qualifier |= (left_qualifiers << 1) | (right_qualifiers >> 1);
  351.  
  352.             /* Adjust the ie_Code field if necessary */
  353.             if (ie->ie_Class == IECLASS_RAWKEY) {
  354.                 /* save and clear IECODE_UP_PREFIX from ie_Code */
  355.                 upstroke = ie->ie_Code & IECODE_UP_PREFIX;
  356.                 ie->ie_Code &= ~IECODE_UP_PREFIX;
  357.  
  358.                 if (ie->ie_Code == RAW_LAMIGA)
  359.                     ie->ie_Code = RAW_RAMIGA;
  360.                 else if (ie->ie_Code == RAW_RAMIGA)
  361.                     ie->ie_Code = RAW_LAMIGA;
  362.  
  363.                 /* restore upstroke */
  364.                 ie->ie_Code |= upstroke;
  365.             }
  366.         } /* end of flipping code */
  367.         ie = ie->ie_NextEvent;
  368.     }
  369. }
  370.  
  371. /*
  372. And that is it!
  373. */